<template>
  <div>
    <button type="button" v-on:click="selectFile()" class="btn btn-white btn-default btn-round">
      <i class="ace-icon fa fa-upload"></i>
      {{text}}
    </button>
    <input class="hidden" type="file" ref="file" v-on:change="uploadFile()" v-bind:id="inputId+'-input'">
  </div>
</template>

<script>
import Toast from "../../public/static/js/toast";
import Loading from "../../public/static/js/loading";
import Tool from "../../public/static/js/tool";

  export default {
    name: 'big-file',
    props: {
      text: {
        default: "上传大文件"
      },
      inputId: {
        default: "file-upload"
      },
      suffixs: {
        default: []
      },
      shardSize: {
        default: 50 * 1024
      },
      url: {
        default: "upload"
      },
      use: {
        default: ""
      },
      afterUpload: {
        type: Function,
        default: null
      },
    },
    data: function () {
      return {
        fileBase: `${process.env.VUE_APP_BASE_URL}/file`,
      }
    },
    methods: {
      uploadFile () {
        let _this = this;
        let formData = new window.FormData();
        let file = _this.$refs.file.files[0];
        // 判断文件大小
        if (file && file.size > 1024*1024*1024) {
          Toast.warning("文件最大只支持1GB");
          return;
        }
        // 判断文件格式
        let suffixs = _this.suffixs;
        let fileName = file.name;
        let suffix = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length).toLowerCase();
        let validateSuffix = false;
        for (let i = 0; i < suffixs.length; i++) {
          if (suffixs[i].toLowerCase() === suffix) {
            validateSuffix = true;
            break;
          }
        }
        if (!validateSuffix) {
          Toast.warning("文件格式不正确！只支持上传：" + suffixs.join(","));
          document.getElementById(_this.inputId + "-input").value=""
          return;
        }

        // 文件分片
        let shardSize = _this.shardSize
        // 分片索引 从1开始
        let shardIndex = 1
        let size = file.size
        // 总片数
        let shardTotal = Math.ceil(size / shardSize)
        // Loading.show()
        // 分片计算文件md5
        Loading.showMessage('文件校验中...')
        _this.md5(file, shardSize).then(e => {
          Loading.hide()
            // key："file"必须和后端controller参数名一致
          formData.append('use', _this.use);
          formData.append('shardIndex', shardIndex)
          formData.append('shardSize', shardSize)
          formData.append('shardTotal', shardTotal)
          formData.append('name', file.name)
          formData.append('suffix', suffix)
          formData.append('size', size)
          formData.append('key', e)
          // 上传文件
          _this.check(formData, file)
        })
      },

      check(param, file) {
        Loading.show()
        let _this = this
        _this.$axios.post(`${_this.fileBase}/${_this.url}/check`, param).then((res) =>{
          Loading.hide()
          if(res.data.success) {
            if(!res.data.data) {
              // 不存在记录
              param.set('shardIndex', 1)
              _this.upload(param, file)
            } else if(res.data.data.shardIndex == res.data.data.shardTotal) {
              // 已存在且上传完成
              Toast.success('文件极速妙传成功')
              $("#" + _this.inputId + "-input").val("");
              _this.afterUpload(res);
            } else {
              //  已存在但未上传完成
              param.set('shardIndex', res.data.data.shardIndex)
              _this.upload(param, file)
            }
          } else {
            $("#" + _this.inputId + "-input").val("");
            Toast.error("文件上传失败")
          }
        }).catch(() => {
          Loading.hide()
          $("#" + _this.inputId + "-input").val("");
          Toast.error("系统错误")
        })
      },

      upload(param, file) {
          let _this = this;
          let shardIndex = param.get("shardIndex")
          let shardTotal = param.get("shardTotal")
          let shardSize = param.get("shardSize")
          let shard = _this.getFileShard(shardIndex, shardSize, file)
          param.set('shard', shard)
          Progress.show(parseInt((shardIndex - 1) * 100 / shardTotal))
          _this.$axios.post(`${_this.fileBase}/${_this.url}`, param).then((res)=>{
            if(res.data.success) {
                Progress.show(parseInt(shardIndex * 100 / shardTotal))
                // Toast.success(_this.text+"成功");
                if (parseInt(shardIndex) < parseInt(shardTotal)) {
                    // 继续上传分片
                    param.set("shardIndex", parseInt(param.get("shardIndex")) + 1)
                    this.upload(param, file)
                } else {
                    // 上传完毕
                    Progress.hide()
                    _this.afterUpload(res);
                    $("#" + _this.inputId + "-input").val("");
                }
            } else {
                Progress.hide()
                Toast.error(_this.text + '失败')
            }
        }).catch(() => {
            $("#" + _this.inputId + "-input").val("");
            Progress.hide()
            Loading.hide()
            Toast.error('系统异常')
        });
      },

      getFileShard (shardIndex, shardSize, file) {
        let start = (shardIndex - 1) * shardSize;	//当前分片起始位置
        let end = Math.min(file.size, parseInt(start) + parseInt(shardSize)); //当前分片结束位置
        console.log(shardIndex, ": ", start, "-->",end)
        let fileShard = file.slice(start, end); //从文件中截取当前的分片数据
        return fileShard;
      },
      selectFile () {
        let _this = this;
        $("#" + _this.inputId + "-input").trigger("click");
      },
      md5(file, chunkSize) {
            return new Promise((resolve, reject) => {
                let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
                let chunks = Math.ceil(file.size / chunkSize);
                let currentChunk = 0;
                let spark = new SparkMD5.ArrayBuffer();
                let fileReader = new FileReader();
            
                fileReader.onload = function(e) {
                    spark.append(e.target.result); 
                    currentChunk++;
                    if (currentChunk < chunks) {
                        loadNext();
                    } else {
                        let md5 = spark.end();
                        resolve(md5);
                    }
                };
            
                fileReader.onerror = function(e) {
                    reject(e);
                };
            
                function loadNext() {
                    let start = currentChunk * chunkSize;
                    let end = start + chunkSize;
                    if (end > file.size){
                        end = file.size;
                    }
                    fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
                }
                loadNext();
            });
        }
    }
  }
</script>
